home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / lib / python2.6 / mailbox.pyc (.txt) < prev    next >
Python Compiled Bytecode  |  2009-11-11  |  75KB  |  2,586 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. '''Read/write support for Maildir, mbox, MH, Babyl, and MMDF mailboxes.'''
  5. import sys
  6. import os
  7. import time
  8. import calendar
  9. import socket
  10. import errno
  11. import copy
  12. import email
  13. import email.message as email
  14. import email.generator as email
  15. import rfc822
  16. import StringIO
  17.  
  18. try:
  19.     if sys.platform == 'os2emx':
  20.         raise ImportError
  21.     sys.platform == 'os2emx'
  22.     import fcntl
  23. except ImportError:
  24.     fcntl = None
  25.  
  26. __all__ = [
  27.     'Mailbox',
  28.     'Maildir',
  29.     'mbox',
  30.     'MH',
  31.     'Babyl',
  32.     'MMDF',
  33.     'Message',
  34.     'MaildirMessage',
  35.     'mboxMessage',
  36.     'MHMessage',
  37.     'BabylMessage',
  38.     'MMDFMessage',
  39.     'UnixMailbox',
  40.     'PortableUnixMailbox',
  41.     'MmdfMailbox',
  42.     'MHMailbox',
  43.     'BabylMailbox']
  44.  
  45. class Mailbox:
  46.     '''A group of messages in a particular place.'''
  47.     
  48.     def __init__(self, path, factory = None, create = True):
  49.         '''Initialize a Mailbox instance.'''
  50.         self._path = os.path.abspath(os.path.expanduser(path))
  51.         self._factory = factory
  52.  
  53.     
  54.     def add(self, message):
  55.         '''Add message and return assigned key.'''
  56.         raise NotImplementedError('Method must be implemented by subclass')
  57.  
  58.     
  59.     def remove(self, key):
  60.         """Remove the keyed message; raise KeyError if it doesn't exist."""
  61.         raise NotImplementedError('Method must be implemented by subclass')
  62.  
  63.     
  64.     def __delitem__(self, key):
  65.         self.remove(key)
  66.  
  67.     
  68.     def discard(self, key):
  69.         '''If the keyed message exists, remove it.'''
  70.         
  71.         try:
  72.             self.remove(key)
  73.         except KeyError:
  74.             pass
  75.  
  76.  
  77.     
  78.     def __setitem__(self, key, message):
  79.         """Replace the keyed message; raise KeyError if it doesn't exist."""
  80.         raise NotImplementedError('Method must be implemented by subclass')
  81.  
  82.     
  83.     def get(self, key, default = None):
  84.         """Return the keyed message, or default if it doesn't exist."""
  85.         
  86.         try:
  87.             return self.__getitem__(key)
  88.         except KeyError:
  89.             return default
  90.  
  91.  
  92.     
  93.     def __getitem__(self, key):
  94.         """Return the keyed message; raise KeyError if it doesn't exist."""
  95.         if not self._factory:
  96.             return self.get_message(key)
  97.         return self._factory(self.get_file(key))
  98.  
  99.     
  100.     def get_message(self, key):
  101.         '''Return a Message representation or raise a KeyError.'''
  102.         raise NotImplementedError('Method must be implemented by subclass')
  103.  
  104.     
  105.     def get_string(self, key):
  106.         '''Return a string representation or raise a KeyError.'''
  107.         raise NotImplementedError('Method must be implemented by subclass')
  108.  
  109.     
  110.     def get_file(self, key):
  111.         '''Return a file-like representation or raise a KeyError.'''
  112.         raise NotImplementedError('Method must be implemented by subclass')
  113.  
  114.     
  115.     def iterkeys(self):
  116.         '''Return an iterator over keys.'''
  117.         raise NotImplementedError('Method must be implemented by subclass')
  118.  
  119.     
  120.     def keys(self):
  121.         '''Return a list of keys.'''
  122.         return list(self.iterkeys())
  123.  
  124.     
  125.     def itervalues(self):
  126.         '''Return an iterator over all messages.'''
  127.         for key in self.iterkeys():
  128.             
  129.             try:
  130.                 value = self[key]
  131.             except KeyError:
  132.                 continue
  133.  
  134.             yield value
  135.         
  136.  
  137.     
  138.     def __iter__(self):
  139.         return self.itervalues()
  140.  
  141.     
  142.     def values(self):
  143.         '''Return a list of messages. Memory intensive.'''
  144.         return list(self.itervalues())
  145.  
  146.     
  147.     def iteritems(self):
  148.         '''Return an iterator over (key, message) tuples.'''
  149.         for key in self.iterkeys():
  150.             
  151.             try:
  152.                 value = self[key]
  153.             except KeyError:
  154.                 continue
  155.  
  156.             yield (key, value)
  157.         
  158.  
  159.     
  160.     def items(self):
  161.         '''Return a list of (key, message) tuples. Memory intensive.'''
  162.         return list(self.iteritems())
  163.  
  164.     
  165.     def has_key(self, key):
  166.         '''Return True if the keyed message exists, False otherwise.'''
  167.         raise NotImplementedError('Method must be implemented by subclass')
  168.  
  169.     
  170.     def __contains__(self, key):
  171.         return self.has_key(key)
  172.  
  173.     
  174.     def __len__(self):
  175.         '''Return a count of messages in the mailbox.'''
  176.         raise NotImplementedError('Method must be implemented by subclass')
  177.  
  178.     
  179.     def clear(self):
  180.         '''Delete all messages.'''
  181.         for key in self.iterkeys():
  182.             self.discard(key)
  183.         
  184.  
  185.     
  186.     def pop(self, key, default = None):
  187.         '''Delete the keyed message and return it, or default.'''
  188.         
  189.         try:
  190.             result = self[key]
  191.         except KeyError:
  192.             return default
  193.  
  194.         self.discard(key)
  195.         return result
  196.  
  197.     
  198.     def popitem(self):
  199.         '''Delete an arbitrary (key, message) pair and return it.'''
  200.         for key in self.iterkeys():
  201.             return (key, self.pop(key))
  202.         else:
  203.             raise KeyError('No messages in mailbox')
  204.  
  205.     
  206.     def update(self, arg = None):
  207.         '''Change the messages that correspond to certain keys.'''
  208.         if hasattr(arg, 'iteritems'):
  209.             source = arg.iteritems()
  210.         elif hasattr(arg, 'items'):
  211.             source = arg.items()
  212.         else:
  213.             source = arg
  214.         bad_key = False
  215.         for key, message in source:
  216.             
  217.             try:
  218.                 self[key] = message
  219.             continue
  220.             except KeyError:
  221.                 bad_key = True
  222.                 continue
  223.             
  224.  
  225.         
  226.         if bad_key:
  227.             raise KeyError('No message with key(s)')
  228.         bad_key
  229.  
  230.     
  231.     def flush(self):
  232.         '''Write any pending changes to the disk.'''
  233.         raise NotImplementedError('Method must be implemented by subclass')
  234.  
  235.     
  236.     def lock(self):
  237.         '''Lock the mailbox.'''
  238.         raise NotImplementedError('Method must be implemented by subclass')
  239.  
  240.     
  241.     def unlock(self):
  242.         '''Unlock the mailbox if it is locked.'''
  243.         raise NotImplementedError('Method must be implemented by subclass')
  244.  
  245.     
  246.     def close(self):
  247.         '''Flush and close the mailbox.'''
  248.         raise NotImplementedError('Method must be implemented by subclass')
  249.  
  250.     
  251.     def _dump_message(self, message, target, mangle_from_ = False):
  252.         '''Dump message contents to target file.'''
  253.         if isinstance(message, email.message.Message):
  254.             buffer = StringIO.StringIO()
  255.             gen = email.generator.Generator(buffer, mangle_from_, 0)
  256.             gen.flatten(message)
  257.             buffer.seek(0)
  258.             target.write(buffer.read().replace('\n', os.linesep))
  259.         elif isinstance(message, str):
  260.             if mangle_from_:
  261.                 message = message.replace('\nFrom ', '\n>From ')
  262.             
  263.             message = message.replace('\n', os.linesep)
  264.             target.write(message)
  265.         elif hasattr(message, 'read'):
  266.             while True:
  267.                 line = message.readline()
  268.                 if line == '':
  269.                     break
  270.                 
  271.                 if mangle_from_ and line.startswith('From '):
  272.                     line = '>From ' + line[5:]
  273.                 
  274.                 line = line.replace('\n', os.linesep)
  275.                 target.write(line)
  276.         else:
  277.             raise TypeError('Invalid message type: %s' % type(message))
  278.         return isinstance(message, email.message.Message)
  279.  
  280.  
  281.  
  282. class Maildir(Mailbox):
  283.     '''A qmail-style Maildir mailbox.'''
  284.     colon = ':'
  285.     
  286.     def __init__(self, dirname, factory = rfc822.Message, create = True):
  287.         '''Initialize a Maildir instance.'''
  288.         Mailbox.__init__(self, dirname, factory, create)
  289.         if not os.path.exists(self._path):
  290.             if create:
  291.                 os.mkdir(self._path, 448)
  292.                 os.mkdir(os.path.join(self._path, 'tmp'), 448)
  293.                 os.mkdir(os.path.join(self._path, 'new'), 448)
  294.                 os.mkdir(os.path.join(self._path, 'cur'), 448)
  295.             else:
  296.                 raise NoSuchMailboxError(self._path)
  297.         create
  298.         self._toc = { }
  299.  
  300.     
  301.     def add(self, message):
  302.         '''Add message and return assigned key.'''
  303.         tmp_file = self._create_tmp()
  304.         
  305.         try:
  306.             self._dump_message(message, tmp_file)
  307.         finally:
  308.             _sync_close(tmp_file)
  309.  
  310.         if isinstance(message, MaildirMessage):
  311.             subdir = message.get_subdir()
  312.             suffix = self.colon + message.get_info()
  313.             if suffix == self.colon:
  314.                 suffix = ''
  315.             
  316.         else:
  317.             subdir = 'new'
  318.             suffix = ''
  319.         uniq = os.path.basename(tmp_file.name).split(self.colon)[0]
  320.         dest = os.path.join(self._path, subdir, uniq + suffix)
  321.         
  322.         try:
  323.             if hasattr(os, 'link'):
  324.                 os.link(tmp_file.name, dest)
  325.                 os.remove(tmp_file.name)
  326.             else:
  327.                 os.rename(tmp_file.name, dest)
  328.         except OSError:
  329.             e = None
  330.             os.remove(tmp_file.name)
  331.             if e.errno == errno.EEXIST:
  332.                 raise ExternalClashError('Name clash with existing message: %s' % dest)
  333.             e.errno == errno.EEXIST
  334.             raise 
  335.  
  336.         if isinstance(message, MaildirMessage):
  337.             os.utime(dest, (os.path.getatime(dest), message.get_date()))
  338.         
  339.         return uniq
  340.  
  341.     
  342.     def remove(self, key):
  343.         """Remove the keyed message; raise KeyError if it doesn't exist."""
  344.         os.remove(os.path.join(self._path, self._lookup(key)))
  345.  
  346.     
  347.     def discard(self, key):
  348.         '''If the keyed message exists, remove it.'''
  349.         
  350.         try:
  351.             self.remove(key)
  352.         except KeyError:
  353.             pass
  354.         except OSError:
  355.             e = None
  356.             if e.errno != errno.ENOENT:
  357.                 raise 
  358.             e.errno != errno.ENOENT
  359.  
  360.  
  361.     
  362.     def __setitem__(self, key, message):
  363.         """Replace the keyed message; raise KeyError if it doesn't exist."""
  364.         old_subpath = self._lookup(key)
  365.         temp_key = self.add(message)
  366.         temp_subpath = self._lookup(temp_key)
  367.         if isinstance(message, MaildirMessage):
  368.             dominant_subpath = temp_subpath
  369.         else:
  370.             dominant_subpath = old_subpath
  371.         subdir = os.path.dirname(dominant_subpath)
  372.         if self.colon in dominant_subpath:
  373.             suffix = self.colon + dominant_subpath.split(self.colon)[-1]
  374.         else:
  375.             suffix = ''
  376.         self.discard(key)
  377.         new_path = os.path.join(self._path, subdir, key + suffix)
  378.         os.rename(os.path.join(self._path, temp_subpath), new_path)
  379.         if isinstance(message, MaildirMessage):
  380.             os.utime(new_path, (os.path.getatime(new_path), message.get_date()))
  381.         
  382.  
  383.     
  384.     def get_message(self, key):
  385.         '''Return a Message representation or raise a KeyError.'''
  386.         subpath = self._lookup(key)
  387.         f = open(os.path.join(self._path, subpath), 'r')
  388.         
  389.         try:
  390.             if self._factory:
  391.                 msg = self._factory(f)
  392.             else:
  393.                 msg = MaildirMessage(f)
  394.         finally:
  395.             f.close()
  396.  
  397.         (subdir, name) = os.path.split(subpath)
  398.         msg.set_subdir(subdir)
  399.         if self.colon in name:
  400.             msg.set_info(name.split(self.colon)[-1])
  401.         
  402.         msg.set_date(os.path.getmtime(os.path.join(self._path, subpath)))
  403.         return msg
  404.  
  405.     
  406.     def get_string(self, key):
  407.         '''Return a string representation or raise a KeyError.'''
  408.         f = open(os.path.join(self._path, self._lookup(key)), 'r')
  409.         
  410.         try:
  411.             return f.read()
  412.         finally:
  413.             f.close()
  414.  
  415.  
  416.     
  417.     def get_file(self, key):
  418.         '''Return a file-like representation or raise a KeyError.'''
  419.         f = open(os.path.join(self._path, self._lookup(key)), 'rb')
  420.         return _ProxyFile(f)
  421.  
  422.     
  423.     def iterkeys(self):
  424.         '''Return an iterator over keys.'''
  425.         self._refresh()
  426.         for key in self._toc:
  427.             
  428.             try:
  429.                 self._lookup(key)
  430.             except KeyError:
  431.                 continue
  432.  
  433.             yield key
  434.         
  435.  
  436.     
  437.     def has_key(self, key):
  438.         '''Return True if the keyed message exists, False otherwise.'''
  439.         self._refresh()
  440.         return key in self._toc
  441.  
  442.     
  443.     def __len__(self):
  444.         '''Return a count of messages in the mailbox.'''
  445.         self._refresh()
  446.         return len(self._toc)
  447.  
  448.     
  449.     def flush(self):
  450.         '''Write any pending changes to disk.'''
  451.         pass
  452.  
  453.     
  454.     def lock(self):
  455.         '''Lock the mailbox.'''
  456.         pass
  457.  
  458.     
  459.     def unlock(self):
  460.         '''Unlock the mailbox if it is locked.'''
  461.         pass
  462.  
  463.     
  464.     def close(self):
  465.         '''Flush and close the mailbox.'''
  466.         pass
  467.  
  468.     
  469.     def list_folders(self):
  470.         '''Return a list of folder names.'''
  471.         result = []
  472.         for entry in os.listdir(self._path):
  473.             if len(entry) > 1 and entry[0] == '.' and os.path.isdir(os.path.join(self._path, entry)):
  474.                 result.append(entry[1:])
  475.                 continue
  476.         
  477.         return result
  478.  
  479.     
  480.     def get_folder(self, folder):
  481.         '''Return a Maildir instance for the named folder.'''
  482.         return Maildir(os.path.join(self._path, '.' + folder), factory = self._factory, create = False)
  483.  
  484.     
  485.     def add_folder(self, folder):
  486.         '''Create a folder and return a Maildir instance representing it.'''
  487.         path = os.path.join(self._path, '.' + folder)
  488.         result = Maildir(path, factory = self._factory)
  489.         maildirfolder_path = os.path.join(path, 'maildirfolder')
  490.         if not os.path.exists(maildirfolder_path):
  491.             os.close(os.open(maildirfolder_path, os.O_CREAT | os.O_WRONLY, 438))
  492.         
  493.         return result
  494.  
  495.     
  496.     def remove_folder(self, folder):
  497.         '''Delete the named folder, which must be empty.'''
  498.         path = os.path.join(self._path, '.' + folder)
  499.         for entry in os.listdir(os.path.join(path, 'new')) + os.listdir(os.path.join(path, 'cur')):
  500.             if len(entry) < 1 or entry[0] != '.':
  501.                 raise NotEmptyError('Folder contains message(s): %s' % folder)
  502.             entry[0] != '.'
  503.         
  504.         for entry in os.listdir(path):
  505.             if entry != 'new' and entry != 'cur' and entry != 'tmp' and os.path.isdir(os.path.join(path, entry)):
  506.                 raise NotEmptyError("Folder contains subdirectory '%s': %s" % (folder, entry))
  507.             os.path.isdir(os.path.join(path, entry))
  508.         
  509.         for root, dirs, files in os.walk(path, topdown = False):
  510.             for entry in files:
  511.                 os.remove(os.path.join(root, entry))
  512.             
  513.             for entry in dirs:
  514.                 os.rmdir(os.path.join(root, entry))
  515.             
  516.         
  517.         os.rmdir(path)
  518.  
  519.     
  520.     def clean(self):
  521.         '''Delete old files in "tmp".'''
  522.         now = time.time()
  523.         for entry in os.listdir(os.path.join(self._path, 'tmp')):
  524.             path = os.path.join(self._path, 'tmp', entry)
  525.             if now - os.path.getatime(path) > 129600:
  526.                 os.remove(path)
  527.                 continue
  528.         
  529.  
  530.     _count = 1
  531.     
  532.     def _create_tmp(self):
  533.         '''Create a file in the tmp subdirectory and open and return it.'''
  534.         now = time.time()
  535.         hostname = socket.gethostname()
  536.         if '/' in hostname:
  537.             hostname = hostname.replace('/', '\\057')
  538.         
  539.         if ':' in hostname:
  540.             hostname = hostname.replace(':', '\\072')
  541.         
  542.         uniq = '%s.M%sP%sQ%s.%s' % (int(now), int((now % 1) * 1e+06), os.getpid(), Maildir._count, hostname)
  543.         path = os.path.join(self._path, 'tmp', uniq)
  544.         
  545.         try:
  546.             os.stat(path)
  547.         except OSError:
  548.             e = None
  549.             if e.errno == errno.ENOENT:
  550.                 Maildir._count += 1
  551.                 
  552.                 try:
  553.                     return _create_carefully(path)
  554.                 except OSError:
  555.                     Maildir
  556.                     e = Maildir
  557.                     if e.errno != errno.EEXIST:
  558.                         raise 
  559.                     e.errno != errno.EEXIST
  560.                 except:
  561.                     Maildir<EXCEPTION MATCH>OSError
  562.                 
  563.  
  564.             Maildir<EXCEPTION MATCH>OSError
  565.             raise 
  566.         except:
  567.             Maildir
  568.  
  569.         raise ExternalClashError('Name clash prevented file creation: %s' % path)
  570.  
  571.     
  572.     def _refresh(self):
  573.         '''Update table of contents mapping.'''
  574.         self._toc = { }
  575.         for subdir in ('new', 'cur'):
  576.             subdir_path = os.path.join(self._path, subdir)
  577.             for entry in os.listdir(subdir_path):
  578.                 p = os.path.join(subdir_path, entry)
  579.                 if os.path.isdir(p):
  580.                     continue
  581.                 
  582.                 uniq = entry.split(self.colon)[0]
  583.                 self._toc[uniq] = os.path.join(subdir, entry)
  584.             
  585.         
  586.  
  587.     
  588.     def _lookup(self, key):
  589.         '''Use TOC to return subpath for given key, or raise a KeyError.'''
  590.         
  591.         try:
  592.             if os.path.exists(os.path.join(self._path, self._toc[key])):
  593.                 return self._toc[key]
  594.         except KeyError:
  595.             pass
  596.  
  597.         self._refresh()
  598.         
  599.         try:
  600.             return self._toc[key]
  601.         except KeyError:
  602.             raise KeyError('No message with key: %s' % key)
  603.  
  604.  
  605.     
  606.     def next(self):
  607.         '''Return the next message in a one-time iteration.'''
  608.         if not hasattr(self, '_onetime_keys'):
  609.             self._onetime_keys = self.iterkeys()
  610.         
  611.         while True:
  612.             
  613.             try:
  614.                 return self[self._onetime_keys.next()]
  615.             continue
  616.             except StopIteration:
  617.                 return None
  618.                 except KeyError:
  619.                     continue
  620.                     continue
  621.                 
  622.                 None<EXCEPTION MATCH>KeyError
  623.             return None
  624.  
  625.  
  626.  
  627.  
  628. class _singlefileMailbox(Mailbox):
  629.     '''A single-file mailbox.'''
  630.     
  631.     def __init__(self, path, factory = None, create = True):
  632.         '''Initialize a single-file mailbox.'''
  633.         Mailbox.__init__(self, path, factory, create)
  634.         
  635.         try:
  636.             f = open(self._path, 'rb+')
  637.         except IOError:
  638.             e = None
  639.             if e.errno == errno.ENOENT:
  640.                 if create:
  641.                     f = open(self._path, 'wb+')
  642.                 else:
  643.                     raise NoSuchMailboxError(self._path)
  644.             create
  645.             if e.errno == errno.EACCES:
  646.                 f = open(self._path, 'rb')
  647.             else:
  648.                 raise 
  649.             e.errno == errno.EACCES
  650.  
  651.         self._file = f
  652.         self._toc = None
  653.         self._next_key = 0
  654.         self._pending = False
  655.         self._locked = False
  656.         self._file_length = None
  657.  
  658.     
  659.     def add(self, message):
  660.         '''Add message and return assigned key.'''
  661.         self._lookup()
  662.         self._toc[self._next_key] = self._append_message(message)
  663.         self._next_key += 1
  664.         self._pending = True
  665.         return self._next_key - 1
  666.  
  667.     
  668.     def remove(self, key):
  669.         """Remove the keyed message; raise KeyError if it doesn't exist."""
  670.         self._lookup(key)
  671.         del self._toc[key]
  672.         self._pending = True
  673.  
  674.     
  675.     def __setitem__(self, key, message):
  676.         """Replace the keyed message; raise KeyError if it doesn't exist."""
  677.         self._lookup(key)
  678.         self._toc[key] = self._append_message(message)
  679.         self._pending = True
  680.  
  681.     
  682.     def iterkeys(self):
  683.         '''Return an iterator over keys.'''
  684.         self._lookup()
  685.         for key in self._toc.keys():
  686.             yield key
  687.         
  688.  
  689.     
  690.     def has_key(self, key):
  691.         '''Return True if the keyed message exists, False otherwise.'''
  692.         self._lookup()
  693.         return key in self._toc
  694.  
  695.     
  696.     def __len__(self):
  697.         '''Return a count of messages in the mailbox.'''
  698.         self._lookup()
  699.         return len(self._toc)
  700.  
  701.     
  702.     def lock(self):
  703.         '''Lock the mailbox.'''
  704.         if not self._locked:
  705.             _lock_file(self._file)
  706.             self._locked = True
  707.         
  708.  
  709.     
  710.     def unlock(self):
  711.         '''Unlock the mailbox if it is locked.'''
  712.         if self._locked:
  713.             _unlock_file(self._file)
  714.             self._locked = False
  715.         
  716.  
  717.     
  718.     def flush(self):
  719.         '''Write any pending changes to disk.'''
  720.         if not self._pending:
  721.             return None
  722.         if not self._toc is not None:
  723.             raise AssertionError
  724.         self._file.seek(0, 2)
  725.         cur_len = self._file.tell()
  726.         if cur_len != self._file_length:
  727.             raise ExternalClashError('Size of mailbox file changed (expected %i, found %i)' % (self._file_length, cur_len))
  728.         cur_len != self._file_length
  729.         new_file = _create_temporary(self._path)
  730.         
  731.         try:
  732.             new_toc = { }
  733.             self._pre_mailbox_hook(new_file)
  734.             for key in sorted(self._toc.keys()):
  735.                 (start, stop) = self._toc[key]
  736.                 self._file.seek(start)
  737.                 self._pre_message_hook(new_file)
  738.                 new_start = new_file.tell()
  739.                 while True:
  740.                     buffer = self._file.read(min(4096, stop - self._file.tell()))
  741.                     new_file.write(buffer)
  742.                     continue
  743.                     None if buffer == '' else self._pending
  744.                 new_toc[key] = (new_start, new_file.tell())
  745.                 self._post_message_hook(new_file)
  746.         except:
  747.             self._toc is not None
  748.             self._pending
  749.             new_file.close()
  750.             os.remove(new_file.name)
  751.             raise 
  752.  
  753.         _sync_close(new_file)
  754.         self._file.close()
  755.         
  756.         try:
  757.             os.rename(new_file.name, self._path)
  758.         except OSError:
  759.             self._toc is not None
  760.             e = self._toc is not None
  761.             self._pending
  762.             if (e.errno == errno.EEXIST or os.name == 'os2') and e.errno == errno.EACCES:
  763.                 os.remove(self._path)
  764.                 os.rename(new_file.name, self._path)
  765.             else:
  766.                 raise 
  767.             e.errno == errno.EACCES
  768.  
  769.         self._file = open(self._path, 'rb+')
  770.         self._toc = new_toc
  771.         self._pending = False
  772.  
  773.     
  774.     def _pre_mailbox_hook(self, f):
  775.         '''Called before writing the mailbox to file f.'''
  776.         pass
  777.  
  778.     
  779.     def _pre_message_hook(self, f):
  780.         '''Called before writing each message to file f.'''
  781.         pass
  782.  
  783.     
  784.     def _post_message_hook(self, f):
  785.         '''Called after writing each message to file f.'''
  786.         pass
  787.  
  788.     
  789.     def close(self):
  790.         '''Flush and close the mailbox.'''
  791.         self.flush()
  792.         if self._locked:
  793.             self.unlock()
  794.         
  795.         self._file.close()
  796.  
  797.     
  798.     def _lookup(self, key = None):
  799.         '''Return (start, stop) or raise KeyError.'''
  800.         if self._toc is None:
  801.             self._generate_toc()
  802.         
  803.         if key is not None:
  804.             
  805.             try:
  806.                 return self._toc[key]
  807.             except KeyError:
  808.                 raise KeyError('No message with key: %s' % key)
  809.             except:
  810.                 None<EXCEPTION MATCH>KeyError
  811.             
  812.  
  813.         None<EXCEPTION MATCH>KeyError
  814.  
  815.     
  816.     def _append_message(self, message):
  817.         '''Append message to mailbox and return (start, stop) offsets.'''
  818.         self._file.seek(0, 2)
  819.         self._pre_message_hook(self._file)
  820.         offsets = self._install_message(message)
  821.         self._post_message_hook(self._file)
  822.         self._file.flush()
  823.         self._file_length = self._file.tell()
  824.         return offsets
  825.  
  826.  
  827.  
  828. class _mboxMMDF(_singlefileMailbox):
  829.     '''An mbox or MMDF mailbox.'''
  830.     _mangle_from_ = True
  831.     
  832.     def get_message(self, key):
  833.         '''Return a Message representation or raise a KeyError.'''
  834.         (start, stop) = self._lookup(key)
  835.         self._file.seek(start)
  836.         from_line = self._file.readline().replace(os.linesep, '')
  837.         string = self._file.read(stop - self._file.tell())
  838.         msg = self._message_factory(string.replace(os.linesep, '\n'))
  839.         msg.set_from(from_line[5:])
  840.         return msg
  841.  
  842.     
  843.     def get_string(self, key, from_ = False):
  844.         '''Return a string representation or raise a KeyError.'''
  845.         (start, stop) = self._lookup(key)
  846.         self._file.seek(start)
  847.         if not from_:
  848.             self._file.readline()
  849.         
  850.         string = self._file.read(stop - self._file.tell())
  851.         return string.replace(os.linesep, '\n')
  852.  
  853.     
  854.     def get_file(self, key, from_ = False):
  855.         '''Return a file-like representation or raise a KeyError.'''
  856.         (start, stop) = self._lookup(key)
  857.         self._file.seek(start)
  858.         if not from_:
  859.             self._file.readline()
  860.         
  861.         return _PartialFile(self._file, self._file.tell(), stop)
  862.  
  863.     
  864.     def _install_message(self, message):
  865.         '''Format a message and blindly write to self._file.'''
  866.         from_line = None
  867.         if isinstance(message, str) and message.startswith('From '):
  868.             newline = message.find('\n')
  869.             if newline != -1:
  870.                 from_line = message[:newline]
  871.                 message = message[newline + 1:]
  872.             else:
  873.                 from_line = message
  874.                 message = ''
  875.         elif isinstance(message, _mboxMMDFMessage):
  876.             from_line = 'From ' + message.get_from()
  877.         elif isinstance(message, email.message.Message):
  878.             from_line = message.get_unixfrom()
  879.         
  880.         if from_line is None:
  881.             from_line = 'From MAILER-DAEMON %s' % time.asctime(time.gmtime())
  882.         
  883.         start = self._file.tell()
  884.         self._file.write(from_line + os.linesep)
  885.         self._dump_message(message, self._file, self._mangle_from_)
  886.         stop = self._file.tell()
  887.         return (start, stop)
  888.  
  889.  
  890.  
  891. class mbox(_mboxMMDF):
  892.     '''A classic mbox mailbox.'''
  893.     _mangle_from_ = True
  894.     
  895.     def __init__(self, path, factory = None, create = True):
  896.         '''Initialize an mbox mailbox.'''
  897.         self._message_factory = mboxMessage
  898.         _mboxMMDF.__init__(self, path, factory, create)
  899.  
  900.     
  901.     def _pre_message_hook(self, f):
  902.         '''Called before writing each message to file f.'''
  903.         if f.tell() != 0:
  904.             f.write(os.linesep)
  905.         
  906.  
  907.     
  908.     def _generate_toc(self):
  909.         '''Generate key-to-(start, stop) table of contents.'''
  910.         starts = []
  911.         stops = []
  912.         self._file.seek(0)
  913.         while True:
  914.             line_pos = self._file.tell()
  915.             line = self._file.readline()
  916.             if line.startswith('From '):
  917.                 if len(stops) < len(starts):
  918.                     stops.append(line_pos - len(os.linesep))
  919.                 
  920.                 starts.append(line_pos)
  921.                 continue
  922.             if line == '':
  923.                 stops.append(line_pos)
  924.                 break
  925.                 continue
  926.         self._toc = dict(enumerate(zip(starts, stops)))
  927.         self._next_key = len(self._toc)
  928.         self._file_length = self._file.tell()
  929.  
  930.  
  931.  
  932. class MMDF(_mboxMMDF):
  933.     '''An MMDF mailbox.'''
  934.     
  935.     def __init__(self, path, factory = None, create = True):
  936.         '''Initialize an MMDF mailbox.'''
  937.         self._message_factory = MMDFMessage
  938.         _mboxMMDF.__init__(self, path, factory, create)
  939.  
  940.     
  941.     def _pre_message_hook(self, f):
  942.         '''Called before writing each message to file f.'''
  943.         f.write('\x01\x01\x01\x01' + os.linesep)
  944.  
  945.     
  946.     def _post_message_hook(self, f):
  947.         '''Called after writing each message to file f.'''
  948.         f.write(os.linesep + '\x01\x01\x01\x01' + os.linesep)
  949.  
  950.     
  951.     def _generate_toc(self):
  952.         '''Generate key-to-(start, stop) table of contents.'''
  953.         starts = []
  954.         stops = []
  955.         self._file.seek(0)
  956.         next_pos = 0
  957.         while True:
  958.             line_pos = next_pos
  959.             line = self._file.readline()
  960.             next_pos = self._file.tell()
  961.             if line.startswith('\x01\x01\x01\x01' + os.linesep):
  962.                 starts.append(next_pos)
  963.                 while True:
  964.                     line_pos = next_pos
  965.                     line = self._file.readline()
  966.                     next_pos = self._file.tell()
  967.                     if line == '\x01\x01\x01\x01' + os.linesep:
  968.                         stops.append(line_pos - len(os.linesep))
  969.                         break
  970.                         continue
  971.                     if line == '':
  972.                         stops.append(line_pos)
  973.                         break
  974.                         continue
  975.                 continue
  976.             if line == '':
  977.                 break
  978.                 continue
  979.         self._toc = dict(enumerate(zip(starts, stops)))
  980.         self._next_key = len(self._toc)
  981.         self._file.seek(0, 2)
  982.         self._file_length = self._file.tell()
  983.  
  984.  
  985.  
  986. class MH(Mailbox):
  987.     '''An MH mailbox.'''
  988.     
  989.     def __init__(self, path, factory = None, create = True):
  990.         '''Initialize an MH instance.'''
  991.         Mailbox.__init__(self, path, factory, create)
  992.         if not os.path.exists(self._path):
  993.             if create:
  994.                 os.mkdir(self._path, 448)
  995.                 os.close(os.open(os.path.join(self._path, '.mh_sequences'), os.O_CREAT | os.O_EXCL | os.O_WRONLY, 384))
  996.             else:
  997.                 raise NoSuchMailboxError(self._path)
  998.         create
  999.         self._locked = False
  1000.  
  1001.     
  1002.     def add(self, message):
  1003.         '''Add message and return assigned key.'''
  1004.         keys = self.keys()
  1005.         if len(keys) == 0:
  1006.             new_key = 1
  1007.         else:
  1008.             new_key = max(keys) + 1
  1009.         new_path = os.path.join(self._path, str(new_key))
  1010.         f = _create_carefully(new_path)
  1011.         
  1012.         try:
  1013.             if self._locked:
  1014.                 _lock_file(f)
  1015.             
  1016.             
  1017.             try:
  1018.                 self._dump_message(message, f)
  1019.                 if isinstance(message, MHMessage):
  1020.                     self._dump_sequences(message, new_key)
  1021.             finally:
  1022.                 if self._locked:
  1023.                     _unlock_file(f)
  1024.                 
  1025.  
  1026.         finally:
  1027.             _sync_close(f)
  1028.  
  1029.         return new_key
  1030.  
  1031.     
  1032.     def remove(self, key):
  1033.         """Remove the keyed message; raise KeyError if it doesn't exist."""
  1034.         path = os.path.join(self._path, str(key))
  1035.         
  1036.         try:
  1037.             f = open(path, 'rb+')
  1038.         except IOError:
  1039.             e = None
  1040.             if e.errno == errno.ENOENT:
  1041.                 raise KeyError('No message with key: %s' % key)
  1042.             e.errno == errno.ENOENT
  1043.             raise 
  1044.  
  1045.         
  1046.         try:
  1047.             if self._locked:
  1048.                 _lock_file(f)
  1049.             
  1050.             
  1051.             try:
  1052.                 f.close()
  1053.                 os.remove(os.path.join(self._path, str(key)))
  1054.             finally:
  1055.                 if self._locked:
  1056.                     _unlock_file(f)
  1057.                 
  1058.  
  1059.         finally:
  1060.             f.close()
  1061.  
  1062.  
  1063.     
  1064.     def __setitem__(self, key, message):
  1065.         """Replace the keyed message; raise KeyError if it doesn't exist."""
  1066.         path = os.path.join(self._path, str(key))
  1067.         
  1068.         try:
  1069.             f = open(path, 'rb+')
  1070.         except IOError:
  1071.             e = None
  1072.             if e.errno == errno.ENOENT:
  1073.                 raise KeyError('No message with key: %s' % key)
  1074.             e.errno == errno.ENOENT
  1075.             raise 
  1076.  
  1077.         
  1078.         try:
  1079.             if self._locked:
  1080.                 _lock_file(f)
  1081.             
  1082.             
  1083.             try:
  1084.                 os.close(os.open(path, os.O_WRONLY | os.O_TRUNC))
  1085.                 self._dump_message(message, f)
  1086.                 if isinstance(message, MHMessage):
  1087.                     self._dump_sequences(message, key)
  1088.             finally:
  1089.                 if self._locked:
  1090.                     _unlock_file(f)
  1091.                 
  1092.  
  1093.         finally:
  1094.             _sync_close(f)
  1095.  
  1096.  
  1097.     
  1098.     def get_message(self, key):
  1099.         '''Return a Message representation or raise a KeyError.'''
  1100.         
  1101.         try:
  1102.             if self._locked:
  1103.                 f = open(os.path.join(self._path, str(key)), 'r+')
  1104.             else:
  1105.                 f = open(os.path.join(self._path, str(key)), 'r')
  1106.         except IOError:
  1107.             e = None
  1108.             if e.errno == errno.ENOENT:
  1109.                 raise KeyError('No message with key: %s' % key)
  1110.             e.errno == errno.ENOENT
  1111.             raise 
  1112.  
  1113.         
  1114.         try:
  1115.             if self._locked:
  1116.                 _lock_file(f)
  1117.             
  1118.             
  1119.             try:
  1120.                 msg = MHMessage(f)
  1121.             finally:
  1122.                 if self._locked:
  1123.                     _unlock_file(f)
  1124.                 
  1125.  
  1126.         finally:
  1127.             f.close()
  1128.  
  1129.         for name, key_list in self.get_sequences().iteritems():
  1130.             if key in key_list:
  1131.                 msg.add_sequence(name)
  1132.                 continue
  1133.         
  1134.         return msg
  1135.  
  1136.     
  1137.     def get_string(self, key):
  1138.         '''Return a string representation or raise a KeyError.'''
  1139.         
  1140.         try:
  1141.             if self._locked:
  1142.                 f = open(os.path.join(self._path, str(key)), 'r+')
  1143.             else:
  1144.                 f = open(os.path.join(self._path, str(key)), 'r')
  1145.         except IOError:
  1146.             e = None
  1147.             if e.errno == errno.ENOENT:
  1148.                 raise KeyError('No message with key: %s' % key)
  1149.             e.errno == errno.ENOENT
  1150.             raise 
  1151.  
  1152.         
  1153.         try:
  1154.             if self._locked:
  1155.                 _lock_file(f)
  1156.             
  1157.             
  1158.             try:
  1159.                 return f.read()
  1160.             finally:
  1161.                 if self._locked:
  1162.                     _unlock_file(f)
  1163.                 
  1164.  
  1165.         finally:
  1166.             f.close()
  1167.  
  1168.  
  1169.     
  1170.     def get_file(self, key):
  1171.         '''Return a file-like representation or raise a KeyError.'''
  1172.         
  1173.         try:
  1174.             f = open(os.path.join(self._path, str(key)), 'rb')
  1175.         except IOError:
  1176.             e = None
  1177.             if e.errno == errno.ENOENT:
  1178.                 raise KeyError('No message with key: %s' % key)
  1179.             e.errno == errno.ENOENT
  1180.             raise 
  1181.  
  1182.         return _ProxyFile(f)
  1183.  
  1184.     
  1185.     def iterkeys(self):
  1186.         '''Return an iterator over keys.'''
  1187.         return iter(sorted((lambda .0: for entry in .0:
  1188. if entry.isdigit():
  1189. int(entry)continue)(os.listdir(self._path))))
  1190.  
  1191.     
  1192.     def has_key(self, key):
  1193.         '''Return True if the keyed message exists, False otherwise.'''
  1194.         return os.path.exists(os.path.join(self._path, str(key)))
  1195.  
  1196.     
  1197.     def __len__(self):
  1198.         '''Return a count of messages in the mailbox.'''
  1199.         return len(list(self.iterkeys()))
  1200.  
  1201.     
  1202.     def lock(self):
  1203.         '''Lock the mailbox.'''
  1204.         if not self._locked:
  1205.             self._file = open(os.path.join(self._path, '.mh_sequences'), 'rb+')
  1206.             _lock_file(self._file)
  1207.             self._locked = True
  1208.         
  1209.  
  1210.     
  1211.     def unlock(self):
  1212.         '''Unlock the mailbox if it is locked.'''
  1213.         if self._locked:
  1214.             _unlock_file(self._file)
  1215.             _sync_close(self._file)
  1216.             del self._file
  1217.             self._locked = False
  1218.         
  1219.  
  1220.     
  1221.     def flush(self):
  1222.         '''Write any pending changes to the disk.'''
  1223.         pass
  1224.  
  1225.     
  1226.     def close(self):
  1227.         '''Flush and close the mailbox.'''
  1228.         if self._locked:
  1229.             self.unlock()
  1230.         
  1231.  
  1232.     
  1233.     def list_folders(self):
  1234.         '''Return a list of folder names.'''
  1235.         result = []
  1236.         for entry in os.listdir(self._path):
  1237.             if os.path.isdir(os.path.join(self._path, entry)):
  1238.                 result.append(entry)
  1239.                 continue
  1240.         
  1241.         return result
  1242.  
  1243.     
  1244.     def get_folder(self, folder):
  1245.         '''Return an MH instance for the named folder.'''
  1246.         return MH(os.path.join(self._path, folder), factory = self._factory, create = False)
  1247.  
  1248.     
  1249.     def add_folder(self, folder):
  1250.         '''Create a folder and return an MH instance representing it.'''
  1251.         return MH(os.path.join(self._path, folder), factory = self._factory)
  1252.  
  1253.     
  1254.     def remove_folder(self, folder):
  1255.         '''Delete the named folder, which must be empty.'''
  1256.         path = os.path.join(self._path, folder)
  1257.         entries = os.listdir(path)
  1258.         if entries == [
  1259.             '.mh_sequences']:
  1260.             os.remove(os.path.join(path, '.mh_sequences'))
  1261.         elif entries == []:
  1262.             pass
  1263.         else:
  1264.             raise NotEmptyError('Folder not empty: %s' % self._path)
  1265.         (entries == [
  1266.             '.mh_sequences']).rmdir(path)
  1267.  
  1268.     
  1269.     def get_sequences(self):
  1270.         '''Return a name-to-key-list dictionary to define each sequence.'''
  1271.         results = { }
  1272.         f = open(os.path.join(self._path, '.mh_sequences'), 'r')
  1273.         
  1274.         try:
  1275.             all_keys = set(self.keys())
  1276.             for line in f:
  1277.                 
  1278.                 try:
  1279.                     (name, contents) = line.split(':')
  1280.                     keys = set()
  1281.                     for spec in contents.split():
  1282.                         if spec.isdigit():
  1283.                             keys.add(int(spec))
  1284.                             continue
  1285.                         (start, stop) = (lambda .0: for x in .0:
  1286. int(x))(spec.split('-'))
  1287.                         keys.update(range(start, stop + 1))
  1288.                     
  1289.                     results[name] = _[1]
  1290.                     if len(results[name]) == 0:
  1291.                         del results[name]
  1292.                 continue
  1293.                 except ValueError:
  1294.                     raise FormatError('Invalid sequence specification: %s' % line.rstrip())
  1295.                     continue
  1296.                 
  1297.  
  1298.         finally:
  1299.             f.close()
  1300.  
  1301.         return results
  1302.  
  1303.     
  1304.     def set_sequences(self, sequences):
  1305.         '''Set sequences using the given name-to-key-list dictionary.'''
  1306.         f = open(os.path.join(self._path, '.mh_sequences'), 'r+')
  1307.         
  1308.         try:
  1309.             os.close(os.open(f.name, os.O_WRONLY | os.O_TRUNC))
  1310.             for name, keys in sequences.iteritems():
  1311.                 if len(keys) == 0:
  1312.                     continue
  1313.                 
  1314.                 f.write('%s:' % name)
  1315.                 prev = None
  1316.                 completing = False
  1317.                 for key in sorted(set(keys)):
  1318.                     if key - 1 == prev:
  1319.                         if not completing:
  1320.                             completing = True
  1321.                             f.write('-')
  1322.                         
  1323.                     elif completing:
  1324.                         completing = False
  1325.                         f.write('%s %s' % (prev, key))
  1326.                     else:
  1327.                         f.write(' %s' % key)
  1328.                     prev = key
  1329.                 
  1330.                 if completing:
  1331.                     f.write(str(prev) + '\n')
  1332.                     continue
  1333.                 f.write('\n')
  1334.         finally:
  1335.             _sync_close(f)
  1336.  
  1337.  
  1338.     
  1339.     def pack(self):
  1340.         '''Re-name messages to eliminate numbering gaps. Invalidates keys.'''
  1341.         sequences = self.get_sequences()
  1342.         prev = 0
  1343.         changes = []
  1344.         for key in self.iterkeys():
  1345.             if key - 1 != prev:
  1346.                 changes.append((key, prev + 1))
  1347.                 if hasattr(os, 'link'):
  1348.                     os.link(os.path.join(self._path, str(key)), os.path.join(self._path, str(prev + 1)))
  1349.                     os.unlink(os.path.join(self._path, str(key)))
  1350.                 else:
  1351.                     os.rename(os.path.join(self._path, str(key)), os.path.join(self._path, str(prev + 1)))
  1352.             
  1353.             prev += 1
  1354.         
  1355.         self._next_key = prev + 1
  1356.         if len(changes) == 0:
  1357.             return None
  1358.         for name, key_list in sequences.items():
  1359.             for old, new in changes:
  1360.                 if old in key_list:
  1361.                     key_list[key_list.index(old)] = new
  1362.                     continue
  1363.                 len(changes) == 0
  1364.             
  1365.         
  1366.         self.set_sequences(sequences)
  1367.  
  1368.     
  1369.     def _dump_sequences(self, message, key):
  1370.         '''Inspect a new MHMessage and update sequences appropriately.'''
  1371.         pending_sequences = message.get_sequences()
  1372.         all_sequences = self.get_sequences()
  1373.         for name, key_list in all_sequences.iteritems():
  1374.             if name in pending_sequences:
  1375.                 key_list.append(key)
  1376.                 continue
  1377.             if key in key_list:
  1378.                 del key_list[key_list.index(key)]
  1379.                 continue
  1380.         
  1381.         for sequence in pending_sequences:
  1382.             if sequence not in all_sequences:
  1383.                 all_sequences[sequence] = [
  1384.                     key]
  1385.                 continue
  1386.         
  1387.         self.set_sequences(all_sequences)
  1388.  
  1389.  
  1390.  
  1391. class Babyl(_singlefileMailbox):
  1392.     '''An Rmail-style Babyl mailbox.'''
  1393.     _special_labels = frozenset(('unseen', 'deleted', 'filed', 'answered', 'forwarded', 'edited', 'resent'))
  1394.     
  1395.     def __init__(self, path, factory = None, create = True):
  1396.         '''Initialize a Babyl mailbox.'''
  1397.         _singlefileMailbox.__init__(self, path, factory, create)
  1398.         self._labels = { }
  1399.  
  1400.     
  1401.     def add(self, message):
  1402.         '''Add message and return assigned key.'''
  1403.         key = _singlefileMailbox.add(self, message)
  1404.         if isinstance(message, BabylMessage):
  1405.             self._labels[key] = message.get_labels()
  1406.         
  1407.         return key
  1408.  
  1409.     
  1410.     def remove(self, key):
  1411.         """Remove the keyed message; raise KeyError if it doesn't exist."""
  1412.         _singlefileMailbox.remove(self, key)
  1413.         if key in self._labels:
  1414.             del self._labels[key]
  1415.         
  1416.  
  1417.     
  1418.     def __setitem__(self, key, message):
  1419.         """Replace the keyed message; raise KeyError if it doesn't exist."""
  1420.         _singlefileMailbox.__setitem__(self, key, message)
  1421.         if isinstance(message, BabylMessage):
  1422.             self._labels[key] = message.get_labels()
  1423.         
  1424.  
  1425.     
  1426.     def get_message(self, key):
  1427.         '''Return a Message representation or raise a KeyError.'''
  1428.         (start, stop) = self._lookup(key)
  1429.         self._file.seek(start)
  1430.         self._file.readline()
  1431.         original_headers = StringIO.StringIO()
  1432.         while True:
  1433.             line = self._file.readline()
  1434.             if line == '*** EOOH ***' + os.linesep or line == '':
  1435.                 break
  1436.             
  1437.             original_headers.write(line.replace(os.linesep, '\n'))
  1438.         visible_headers = StringIO.StringIO()
  1439.         while True:
  1440.             line = self._file.readline()
  1441.             if line == os.linesep or line == '':
  1442.                 break
  1443.             
  1444.             visible_headers.write(line.replace(os.linesep, '\n'))
  1445.         body = self._file.read(stop - self._file.tell()).replace(os.linesep, '\n')
  1446.         msg = BabylMessage(original_headers.getvalue() + body)
  1447.         msg.set_visible(visible_headers.getvalue())
  1448.         if key in self._labels:
  1449.             msg.set_labels(self._labels[key])
  1450.         
  1451.         return msg
  1452.  
  1453.     
  1454.     def get_string(self, key):
  1455.         '''Return a string representation or raise a KeyError.'''
  1456.         (start, stop) = self._lookup(key)
  1457.         self._file.seek(start)
  1458.         self._file.readline()
  1459.         original_headers = StringIO.StringIO()
  1460.         while True:
  1461.             line = self._file.readline()
  1462.             if line == '*** EOOH ***' + os.linesep or line == '':
  1463.                 break
  1464.             
  1465.             original_headers.write(line.replace(os.linesep, '\n'))
  1466.         while True:
  1467.             line = self._file.readline()
  1468.             if line == os.linesep or line == '':
  1469.                 break
  1470.                 continue
  1471.         return original_headers.getvalue() + self._file.read(stop - self._file.tell()).replace(os.linesep, '\n')
  1472.  
  1473.     
  1474.     def get_file(self, key):
  1475.         '''Return a file-like representation or raise a KeyError.'''
  1476.         return StringIO.StringIO(self.get_string(key).replace('\n', os.linesep))
  1477.  
  1478.     
  1479.     def get_labels(self):
  1480.         '''Return a list of user-defined labels in the mailbox.'''
  1481.         self._lookup()
  1482.         labels = set()
  1483.         for label_list in self._labels.values():
  1484.             labels.update(label_list)
  1485.         
  1486.         labels.difference_update(self._special_labels)
  1487.         return list(labels)
  1488.  
  1489.     
  1490.     def _generate_toc(self):
  1491.         '''Generate key-to-(start, stop) table of contents.'''
  1492.         starts = []
  1493.         stops = []
  1494.         self._file.seek(0)
  1495.         next_pos = 0
  1496.         label_lists = []
  1497.         while True:
  1498.             line_pos = next_pos
  1499.             line = self._file.readline()
  1500.             next_pos = self._file.tell()
  1501.             if line == '\x1f\x0c' + os.linesep:
  1502.                 if len(stops) < len(starts):
  1503.                     stops.append(line_pos - len(os.linesep))
  1504.                 
  1505.                 starts.append(next_pos)
  1506.                 labels = _[1]
  1507.                 label_lists.append(labels)
  1508.                 continue
  1509.             []
  1510.             if line == '\x1f' or line == '\x1f' + os.linesep:
  1511.                 if len(stops) < len(starts):
  1512.                     stops.append(line_pos - len(os.linesep))
  1513.                 
  1514.             len(stops) < len(starts)
  1515.             if line == '':
  1516.                 stops.append(line_pos - len(os.linesep))
  1517.                 break
  1518.                 continue
  1519.             []
  1520.         self._toc = dict(enumerate(zip(starts, stops)))
  1521.         self._labels = dict(enumerate(label_lists))
  1522.         self._next_key = len(self._toc)
  1523.         self._file.seek(0, 2)
  1524.         self._file_length = self._file.tell()
  1525.  
  1526.     
  1527.     def _pre_mailbox_hook(self, f):
  1528.         '''Called before writing the mailbox to file f.'''
  1529.         f.write('BABYL OPTIONS:%sVersion: 5%sLabels:%s%s\x1f' % (os.linesep, os.linesep, ','.join(self.get_labels()), os.linesep))
  1530.  
  1531.     
  1532.     def _pre_message_hook(self, f):
  1533.         '''Called before writing each message to file f.'''
  1534.         f.write('\x0c' + os.linesep)
  1535.  
  1536.     
  1537.     def _post_message_hook(self, f):
  1538.         '''Called after writing each message to file f.'''
  1539.         f.write(os.linesep + '\x1f')
  1540.  
  1541.     
  1542.     def _install_message(self, message):
  1543.         '''Write message contents and return (start, stop).'''
  1544.         start = self._file.tell()
  1545.         if isinstance(message, BabylMessage):
  1546.             special_labels = []
  1547.             labels = []
  1548.             for label in message.get_labels():
  1549.                 if label in self._special_labels:
  1550.                     special_labels.append(label)
  1551.                     continue
  1552.                 labels.append(label)
  1553.             
  1554.             self._file.write('1')
  1555.             for label in special_labels:
  1556.                 self._file.write(', ' + label)
  1557.             
  1558.             self._file.write(',,')
  1559.             for label in labels:
  1560.                 self._file.write(' ' + label + ',')
  1561.             
  1562.             self._file.write(os.linesep)
  1563.         else:
  1564.             self._file.write('1,,' + os.linesep)
  1565.         if isinstance(message, email.message.Message):
  1566.             orig_buffer = StringIO.StringIO()
  1567.             orig_generator = email.generator.Generator(orig_buffer, False, 0)
  1568.             orig_generator.flatten(message)
  1569.             orig_buffer.seek(0)
  1570.             while True:
  1571.                 line = orig_buffer.readline()
  1572.                 self._file.write(line.replace('\n', os.linesep))
  1573.                 if line == '\n' or line == '':
  1574.                     break
  1575.                     continue
  1576.             self._file.write('*** EOOH ***' + os.linesep)
  1577.             if isinstance(message, BabylMessage):
  1578.                 vis_buffer = StringIO.StringIO()
  1579.                 vis_generator = email.generator.Generator(vis_buffer, False, 0)
  1580.                 vis_generator.flatten(message.get_visible())
  1581.                 while True:
  1582.                     line = vis_buffer.readline()
  1583.                     self._file.write(line.replace('\n', os.linesep))
  1584.                     if line == '\n' or line == '':
  1585.                         break
  1586.                         continue
  1587.             else:
  1588.                 orig_buffer.seek(0)
  1589.                 while True:
  1590.                     line = orig_buffer.readline()
  1591.                     self._file.write(line.replace('\n', os.linesep))
  1592.                     if line == '\n' or line == '':
  1593.                         break
  1594.                         continue
  1595.             while True:
  1596.                 buffer = orig_buffer.read(4096)
  1597.                 if buffer == '':
  1598.                     break
  1599.                 
  1600.                 self._file.write(buffer.replace('\n', os.linesep))
  1601.         elif isinstance(message, str):
  1602.             body_start = message.find('\n\n') + 2
  1603.             if body_start - 2 != -1:
  1604.                 self._file.write(message[:body_start].replace('\n', os.linesep))
  1605.                 self._file.write('*** EOOH ***' + os.linesep)
  1606.                 self._file.write(message[:body_start].replace('\n', os.linesep))
  1607.                 self._file.write(message[body_start:].replace('\n', os.linesep))
  1608.             else:
  1609.                 self._file.write('*** EOOH ***' + os.linesep + os.linesep)
  1610.                 self._file.write(message.replace('\n', os.linesep))
  1611.         elif hasattr(message, 'readline'):
  1612.             original_pos = message.tell()
  1613.             first_pass = True
  1614.             while True:
  1615.                 line = message.readline()
  1616.                 self._file.write(line.replace('\n', os.linesep))
  1617.                 if line == '\n' or line == '':
  1618.                     self._file.write('*** EOOH ***' + os.linesep)
  1619.                     if first_pass:
  1620.                         first_pass = False
  1621.                         message.seek(original_pos)
  1622.                     else:
  1623.                         break
  1624.                 first_pass
  1625.             while True:
  1626.                 buffer = message.read(4096)
  1627.                 if buffer == '':
  1628.                     break
  1629.                 
  1630.                 self._file.write(buffer.replace('\n', os.linesep))
  1631.         else:
  1632.             raise TypeError('Invalid message type: %s' % type(message))
  1633.         stop = isinstance(message, email.message.Message)._file.tell()
  1634.         return (start, stop)
  1635.  
  1636.  
  1637.  
  1638. class Message(email.message.Message):
  1639.     '''Message with mailbox-format-specific properties.'''
  1640.     
  1641.     def __init__(self, message = None):
  1642.         '''Initialize a Message instance.'''
  1643.         if isinstance(message, email.message.Message):
  1644.             self._become_message(copy.deepcopy(message))
  1645.             if isinstance(message, Message):
  1646.                 message._explain_to(self)
  1647.             
  1648.         elif isinstance(message, str):
  1649.             self._become_message(email.message_from_string(message))
  1650.         elif hasattr(message, 'read'):
  1651.             self._become_message(email.message_from_file(message))
  1652.         elif message is None:
  1653.             email.message.Message.__init__(self)
  1654.         else:
  1655.             raise TypeError('Invalid message type: %s' % type(message))
  1656.         return isinstance(message, email.message.Message)
  1657.  
  1658.     
  1659.     def _become_message(self, message):
  1660.         '''Assume the non-format-specific state of message.'''
  1661.         for name in ('_headers', '_unixfrom', '_payload', '_charset', 'preamble', 'epilogue', 'defects', '_default_type'):
  1662.             self.__dict__[name] = message.__dict__[name]
  1663.         
  1664.  
  1665.     
  1666.     def _explain_to(self, message):
  1667.         '''Copy format-specific state to message insofar as possible.'''
  1668.         if isinstance(message, Message):
  1669.             return None
  1670.         raise TypeError('Cannot convert to specified type')
  1671.  
  1672.  
  1673.  
  1674. class MaildirMessage(Message):
  1675.     '''Message with Maildir-specific properties.'''
  1676.     
  1677.     def __init__(self, message = None):
  1678.         '''Initialize a MaildirMessage instance.'''
  1679.         self._subdir = 'new'
  1680.         self._info = ''
  1681.         self._date = time.time()
  1682.         Message.__init__(self, message)
  1683.  
  1684.     
  1685.     def get_subdir(self):
  1686.         """Return 'new' or 'cur'."""
  1687.         return self._subdir
  1688.  
  1689.     
  1690.     def set_subdir(self, subdir):
  1691.         """Set subdir to 'new' or 'cur'."""
  1692.         if subdir == 'new' or subdir == 'cur':
  1693.             self._subdir = subdir
  1694.         else:
  1695.             raise ValueError("subdir must be 'new' or 'cur': %s" % subdir)
  1696.         return subdir == 'cur'
  1697.  
  1698.     
  1699.     def get_flags(self):
  1700.         '''Return as a string the flags that are set.'''
  1701.         if self._info.startswith('2,'):
  1702.             return self._info[2:]
  1703.         return ''
  1704.  
  1705.     
  1706.     def set_flags(self, flags):
  1707.         '''Set the given flags and unset all others.'''
  1708.         self._info = '2,' + ''.join(sorted(flags))
  1709.  
  1710.     
  1711.     def add_flag(self, flag):
  1712.         '''Set the given flag(s) without changing others.'''
  1713.         self.set_flags(''.join(set(self.get_flags()) | set(flag)))
  1714.  
  1715.     
  1716.     def remove_flag(self, flag):
  1717.         '''Unset the given string flag(s) without changing others.'''
  1718.         if self.get_flags() != '':
  1719.             self.set_flags(''.join(set(self.get_flags()) - set(flag)))
  1720.         
  1721.  
  1722.     
  1723.     def get_date(self):
  1724.         '''Return delivery date of message, in seconds since the epoch.'''
  1725.         return self._date
  1726.  
  1727.     
  1728.     def set_date(self, date):
  1729.         '''Set delivery date of message, in seconds since the epoch.'''
  1730.         
  1731.         try:
  1732.             self._date = float(date)
  1733.         except ValueError:
  1734.             raise TypeError("can't convert to float: %s" % date)
  1735.  
  1736.  
  1737.     
  1738.     def get_info(self):
  1739.         '''Get the message\'s "info" as a string.'''
  1740.         return self._info
  1741.  
  1742.     
  1743.     def set_info(self, info):
  1744.         '''Set the message\'s "info" string.'''
  1745.         if isinstance(info, str):
  1746.             self._info = info
  1747.         else:
  1748.             raise TypeError('info must be a string: %s' % type(info))
  1749.         return isinstance(info, str)
  1750.  
  1751.     
  1752.     def _explain_to(self, message):
  1753.         '''Copy Maildir-specific state to message insofar as possible.'''
  1754.         if isinstance(message, MaildirMessage):
  1755.             message.set_flags(self.get_flags())
  1756.             message.set_subdir(self.get_subdir())
  1757.             message.set_date(self.get_date())
  1758.         elif isinstance(message, _mboxMMDFMessage):
  1759.             flags = set(self.get_flags())
  1760.             if 'S' in flags:
  1761.                 message.add_flag('R')
  1762.             
  1763.             if self.get_subdir() == 'cur':
  1764.                 message.add_flag('O')
  1765.             
  1766.             if 'T' in flags:
  1767.                 message.add_flag('D')
  1768.             
  1769.             if 'F' in flags:
  1770.                 message.add_flag('F')
  1771.             
  1772.             if 'R' in flags:
  1773.                 message.add_flag('A')
  1774.             
  1775.             message.set_from('MAILER-DAEMON', time.gmtime(self.get_date()))
  1776.         elif isinstance(message, MHMessage):
  1777.             flags = set(self.get_flags())
  1778.             if 'S' not in flags:
  1779.                 message.add_sequence('unseen')
  1780.             
  1781.             if 'R' in flags:
  1782.                 message.add_sequence('replied')
  1783.             
  1784.             if 'F' in flags:
  1785.                 message.add_sequence('flagged')
  1786.             
  1787.         elif isinstance(message, BabylMessage):
  1788.             flags = set(self.get_flags())
  1789.             if 'S' not in flags:
  1790.                 message.add_label('unseen')
  1791.             
  1792.             if 'T' in flags:
  1793.                 message.add_label('deleted')
  1794.             
  1795.             if 'R' in flags:
  1796.                 message.add_label('answered')
  1797.             
  1798.             if 'P' in flags:
  1799.                 message.add_label('forwarded')
  1800.             
  1801.         elif isinstance(message, Message):
  1802.             pass
  1803.         else:
  1804.             raise TypeError('Cannot convert to specified type: %s' % type(message))
  1805.         return isinstance(message, MaildirMessage)
  1806.  
  1807.  
  1808.  
  1809. class _mboxMMDFMessage(Message):
  1810.     '''Message with mbox- or MMDF-specific properties.'''
  1811.     
  1812.     def __init__(self, message = None):
  1813.         '''Initialize an mboxMMDFMessage instance.'''
  1814.         self.set_from('MAILER-DAEMON', True)
  1815.         if isinstance(message, email.message.Message):
  1816.             unixfrom = message.get_unixfrom()
  1817.             if unixfrom is not None and unixfrom.startswith('From '):
  1818.                 self.set_from(unixfrom[5:])
  1819.             
  1820.         
  1821.         Message.__init__(self, message)
  1822.  
  1823.     
  1824.     def get_from(self):
  1825.         '''Return contents of "From " line.'''
  1826.         return self._from
  1827.  
  1828.     
  1829.     def set_from(self, from_, time_ = None):
  1830.         '''Set "From " line, formatting and appending time_ if specified.'''
  1831.         if time_ is not None:
  1832.             if time_ is True:
  1833.                 time_ = time.gmtime()
  1834.             
  1835.             from_ += ' ' + time.asctime(time_)
  1836.         
  1837.         self._from = from_
  1838.  
  1839.     
  1840.     def get_flags(self):
  1841.         '''Return as a string the flags that are set.'''
  1842.         return self.get('Status', '') + self.get('X-Status', '')
  1843.  
  1844.     
  1845.     def set_flags(self, flags):
  1846.         '''Set the given flags and unset all others.'''
  1847.         flags = set(flags)
  1848.         (status_flags, xstatus_flags) = ('', '')
  1849.         for flag in ('R', 'O'):
  1850.             if flag in flags:
  1851.                 status_flags += flag
  1852.                 flags.remove(flag)
  1853.                 continue
  1854.         
  1855.         for flag in ('D', 'F', 'A'):
  1856.             if flag in flags:
  1857.                 xstatus_flags += flag
  1858.                 flags.remove(flag)
  1859.                 continue
  1860.         
  1861.         xstatus_flags += ''.join(sorted(flags))
  1862.         
  1863.         try:
  1864.             self.replace_header('Status', status_flags)
  1865.         except KeyError:
  1866.             self.add_header('Status', status_flags)
  1867.  
  1868.         
  1869.         try:
  1870.             self.replace_header('X-Status', xstatus_flags)
  1871.         except KeyError:
  1872.             self.add_header('X-Status', xstatus_flags)
  1873.  
  1874.  
  1875.     
  1876.     def add_flag(self, flag):
  1877.         '''Set the given flag(s) without changing others.'''
  1878.         self.set_flags(''.join(set(self.get_flags()) | set(flag)))
  1879.  
  1880.     
  1881.     def remove_flag(self, flag):
  1882.         '''Unset the given string flag(s) without changing others.'''
  1883.         if 'Status' in self or 'X-Status' in self:
  1884.             self.set_flags(''.join(set(self.get_flags()) - set(flag)))
  1885.         
  1886.  
  1887.     
  1888.     def _explain_to(self, message):
  1889.         '''Copy mbox- or MMDF-specific state to message insofar as possible.'''
  1890.         if isinstance(message, MaildirMessage):
  1891.             flags = set(self.get_flags())
  1892.             if 'O' in flags:
  1893.                 message.set_subdir('cur')
  1894.             
  1895.             if 'F' in flags:
  1896.                 message.add_flag('F')
  1897.             
  1898.             if 'A' in flags:
  1899.                 message.add_flag('R')
  1900.             
  1901.             if 'R' in flags:
  1902.                 message.add_flag('S')
  1903.             
  1904.             if 'D' in flags:
  1905.                 message.add_flag('T')
  1906.             
  1907.             del message['status']
  1908.             del message['x-status']
  1909.             maybe_date = ' '.join(self.get_from().split()[-5:])
  1910.             
  1911.             try:
  1912.                 message.set_date(calendar.timegm(time.strptime(maybe_date, '%a %b %d %H:%M:%S %Y')))
  1913.             except (ValueError, OverflowError):
  1914.                 pass
  1915.             except:
  1916.                 None<EXCEPTION MATCH>(ValueError, OverflowError)
  1917.             
  1918.  
  1919.         None<EXCEPTION MATCH>(ValueError, OverflowError)
  1920.         if isinstance(message, _mboxMMDFMessage):
  1921.             message.set_flags(self.get_flags())
  1922.             message.set_from(self.get_from())
  1923.         elif isinstance(message, MHMessage):
  1924.             flags = set(self.get_flags())
  1925.             if 'R' not in flags:
  1926.                 message.add_sequence('unseen')
  1927.             
  1928.             if 'A' in flags:
  1929.                 message.add_sequence('replied')
  1930.             
  1931.             if 'F' in flags:
  1932.                 message.add_sequence('flagged')
  1933.             
  1934.             del message['status']
  1935.             del message['x-status']
  1936.         elif isinstance(message, BabylMessage):
  1937.             flags = set(self.get_flags())
  1938.             if 'R' not in flags:
  1939.                 message.add_label('unseen')
  1940.             
  1941.             if 'D' in flags:
  1942.                 message.add_label('deleted')
  1943.             
  1944.             if 'A' in flags:
  1945.                 message.add_label('answered')
  1946.             
  1947.             del message['status']
  1948.             del message['x-status']
  1949.         elif isinstance(message, Message):
  1950.             pass
  1951.         else:
  1952.             raise TypeError('Cannot convert to specified type: %s' % type(message))
  1953.         return isinstance(message, _mboxMMDFMessage)
  1954.  
  1955.  
  1956.  
  1957. class mboxMessage(_mboxMMDFMessage):
  1958.     '''Message with mbox-specific properties.'''
  1959.     pass
  1960.  
  1961.  
  1962. class MHMessage(Message):
  1963.     '''Message with MH-specific properties.'''
  1964.     
  1965.     def __init__(self, message = None):
  1966.         '''Initialize an MHMessage instance.'''
  1967.         self._sequences = []
  1968.         Message.__init__(self, message)
  1969.  
  1970.     
  1971.     def get_sequences(self):
  1972.         '''Return a list of sequences that include the message.'''
  1973.         return self._sequences[:]
  1974.  
  1975.     
  1976.     def set_sequences(self, sequences):
  1977.         '''Set the list of sequences that include the message.'''
  1978.         self._sequences = list(sequences)
  1979.  
  1980.     
  1981.     def add_sequence(self, sequence):
  1982.         '''Add sequence to list of sequences including the message.'''
  1983.         if isinstance(sequence, str):
  1984.             if sequence not in self._sequences:
  1985.                 self._sequences.append(sequence)
  1986.             
  1987.         else:
  1988.             raise TypeError('sequence must be a string: %s' % type(sequence))
  1989.         return isinstance(sequence, str)
  1990.  
  1991.     
  1992.     def remove_sequence(self, sequence):
  1993.         '''Remove sequence from the list of sequences including the message.'''
  1994.         
  1995.         try:
  1996.             self._sequences.remove(sequence)
  1997.         except ValueError:
  1998.             pass
  1999.  
  2000.  
  2001.     
  2002.     def _explain_to(self, message):
  2003.         '''Copy MH-specific state to message insofar as possible.'''
  2004.         if isinstance(message, MaildirMessage):
  2005.             sequences = set(self.get_sequences())
  2006.             if 'unseen' in sequences:
  2007.                 message.set_subdir('cur')
  2008.             else:
  2009.                 message.set_subdir('cur')
  2010.                 message.add_flag('S')
  2011.             if 'flagged' in sequences:
  2012.                 message.add_flag('F')
  2013.             
  2014.             if 'replied' in sequences:
  2015.                 message.add_flag('R')
  2016.             
  2017.         elif isinstance(message, _mboxMMDFMessage):
  2018.             sequences = set(self.get_sequences())
  2019.             if 'unseen' not in sequences:
  2020.                 message.add_flag('RO')
  2021.             else:
  2022.                 message.add_flag('O')
  2023.             if 'flagged' in sequences:
  2024.                 message.add_flag('F')
  2025.             
  2026.             if 'replied' in sequences:
  2027.                 message.add_flag('A')
  2028.             
  2029.         elif isinstance(message, MHMessage):
  2030.             for sequence in self.get_sequences():
  2031.                 message.add_sequence(sequence)
  2032.             
  2033.         elif isinstance(message, BabylMessage):
  2034.             sequences = set(self.get_sequences())
  2035.             if 'unseen' in sequences:
  2036.                 message.add_label('unseen')
  2037.             
  2038.             if 'replied' in sequences:
  2039.                 message.add_label('answered')
  2040.             
  2041.         elif isinstance(message, Message):
  2042.             pass
  2043.         else:
  2044.             raise TypeError('Cannot convert to specified type: %s' % type(message))
  2045.         return isinstance(message, MaildirMessage)
  2046.  
  2047.  
  2048.  
  2049. class BabylMessage(Message):
  2050.     '''Message with Babyl-specific properties.'''
  2051.     
  2052.     def __init__(self, message = None):
  2053.         '''Initialize an BabylMessage instance.'''
  2054.         self._labels = []
  2055.         self._visible = Message()
  2056.         Message.__init__(self, message)
  2057.  
  2058.     
  2059.     def get_labels(self):
  2060.         '''Return a list of labels on the message.'''
  2061.         return self._labels[:]
  2062.  
  2063.     
  2064.     def set_labels(self, labels):
  2065.         '''Set the list of labels on the message.'''
  2066.         self._labels = list(labels)
  2067.  
  2068.     
  2069.     def add_label(self, label):
  2070.         '''Add label to list of labels on the message.'''
  2071.         if isinstance(label, str):
  2072.             if label not in self._labels:
  2073.                 self._labels.append(label)
  2074.             
  2075.         else:
  2076.             raise TypeError('label must be a string: %s' % type(label))
  2077.         return isinstance(label, str)
  2078.  
  2079.     
  2080.     def remove_label(self, label):
  2081.         '''Remove label from the list of labels on the message.'''
  2082.         
  2083.         try:
  2084.             self._labels.remove(label)
  2085.         except ValueError:
  2086.             pass
  2087.  
  2088.  
  2089.     
  2090.     def get_visible(self):
  2091.         '''Return a Message representation of visible headers.'''
  2092.         return Message(self._visible)
  2093.  
  2094.     
  2095.     def set_visible(self, visible):
  2096.         '''Set the Message representation of visible headers.'''
  2097.         self._visible = Message(visible)
  2098.  
  2099.     
  2100.     def update_visible(self):
  2101.         '''Update and/or sensibly generate a set of visible headers.'''
  2102.         for header in self._visible.keys():
  2103.             if header in self:
  2104.                 self._visible.replace_header(header, self[header])
  2105.                 continue
  2106.             del self._visible[header]
  2107.         
  2108.         for header in ('Date', 'From', 'Reply-To', 'To', 'CC', 'Subject'):
  2109.             if header in self and header not in self._visible:
  2110.                 self._visible[header] = self[header]
  2111.                 continue
  2112.         
  2113.  
  2114.     
  2115.     def _explain_to(self, message):
  2116.         '''Copy Babyl-specific state to message insofar as possible.'''
  2117.         if isinstance(message, MaildirMessage):
  2118.             labels = set(self.get_labels())
  2119.             if 'unseen' in labels:
  2120.                 message.set_subdir('cur')
  2121.             else:
  2122.                 message.set_subdir('cur')
  2123.                 message.add_flag('S')
  2124.             if 'forwarded' in labels or 'resent' in labels:
  2125.                 message.add_flag('P')
  2126.             
  2127.             if 'answered' in labels:
  2128.                 message.add_flag('R')
  2129.             
  2130.             if 'deleted' in labels:
  2131.                 message.add_flag('T')
  2132.             
  2133.         elif isinstance(message, _mboxMMDFMessage):
  2134.             labels = set(self.get_labels())
  2135.             if 'unseen' not in labels:
  2136.                 message.add_flag('RO')
  2137.             else:
  2138.                 message.add_flag('O')
  2139.             if 'deleted' in labels:
  2140.                 message.add_flag('D')
  2141.             
  2142.             if 'answered' in labels:
  2143.                 message.add_flag('A')
  2144.             
  2145.         elif isinstance(message, MHMessage):
  2146.             labels = set(self.get_labels())
  2147.             if 'unseen' in labels:
  2148.                 message.add_sequence('unseen')
  2149.             
  2150.             if 'answered' in labels:
  2151.                 message.add_sequence('replied')
  2152.             
  2153.         elif isinstance(message, BabylMessage):
  2154.             message.set_visible(self.get_visible())
  2155.             for label in self.get_labels():
  2156.                 message.add_label(label)
  2157.             
  2158.         elif isinstance(message, Message):
  2159.             pass
  2160.         else:
  2161.             raise TypeError('Cannot convert to specified type: %s' % type(message))
  2162.         return isinstance(message, MaildirMessage)
  2163.  
  2164.  
  2165.  
  2166. class MMDFMessage(_mboxMMDFMessage):
  2167.     '''Message with MMDF-specific properties.'''
  2168.     pass
  2169.  
  2170.  
  2171. class _ProxyFile:
  2172.     '''A read-only wrapper of a file.'''
  2173.     
  2174.     def __init__(self, f, pos = None):
  2175.         '''Initialize a _ProxyFile.'''
  2176.         self._file = f
  2177.         if pos is None:
  2178.             self._pos = f.tell()
  2179.         else:
  2180.             self._pos = pos
  2181.  
  2182.     
  2183.     def read(self, size = None):
  2184.         '''Read bytes.'''
  2185.         return self._read(size, self._file.read)
  2186.  
  2187.     
  2188.     def readline(self, size = None):
  2189.         '''Read a line.'''
  2190.         return self._read(size, self._file.readline)
  2191.  
  2192.     
  2193.     def readlines(self, sizehint = None):
  2194.         '''Read multiple lines.'''
  2195.         result = []
  2196.         for line in self:
  2197.             result.append(line)
  2198.             if sizehint is not None:
  2199.                 sizehint -= len(line)
  2200.                 if sizehint <= 0:
  2201.                     break
  2202.                 
  2203.             sizehint <= 0
  2204.         
  2205.         return result
  2206.  
  2207.     
  2208.     def __iter__(self):
  2209.         '''Iterate over lines.'''
  2210.         return iter(self.readline, '')
  2211.  
  2212.     
  2213.     def tell(self):
  2214.         '''Return the position.'''
  2215.         return self._pos
  2216.  
  2217.     
  2218.     def seek(self, offset, whence = 0):
  2219.         '''Change position.'''
  2220.         if whence == 1:
  2221.             self._file.seek(self._pos)
  2222.         
  2223.         self._file.seek(offset, whence)
  2224.         self._pos = self._file.tell()
  2225.  
  2226.     
  2227.     def close(self):
  2228.         '''Close the file.'''
  2229.         del self._file
  2230.  
  2231.     
  2232.     def _read(self, size, read_method):
  2233.         '''Read size bytes using read_method.'''
  2234.         if size is None:
  2235.             size = -1
  2236.         
  2237.         self._file.seek(self._pos)
  2238.         result = read_method(size)
  2239.         self._pos = self._file.tell()
  2240.         return result
  2241.  
  2242.  
  2243.  
  2244. class _PartialFile(_ProxyFile):
  2245.     '''A read-only wrapper of part of a file.'''
  2246.     
  2247.     def __init__(self, f, start = None, stop = None):
  2248.         '''Initialize a _PartialFile.'''
  2249.         _ProxyFile.__init__(self, f, start)
  2250.         self._start = start
  2251.         self._stop = stop
  2252.  
  2253.     
  2254.     def tell(self):
  2255.         '''Return the position with respect to start.'''
  2256.         return _ProxyFile.tell(self) - self._start
  2257.  
  2258.     
  2259.     def seek(self, offset, whence = 0):
  2260.         '''Change position, possibly with respect to start or stop.'''
  2261.         if whence == 0:
  2262.             self._pos = self._start
  2263.             whence = 1
  2264.         elif whence == 2:
  2265.             self._pos = self._stop
  2266.             whence = 1
  2267.         
  2268.         _ProxyFile.seek(self, offset, whence)
  2269.  
  2270.     
  2271.     def _read(self, size, read_method):
  2272.         '''Read size bytes using read_method, honoring start and stop.'''
  2273.         remaining = self._stop - self._pos
  2274.         if remaining <= 0:
  2275.             return ''
  2276.         if size is None and size < 0 or size > remaining:
  2277.             size = remaining
  2278.         
  2279.         return _ProxyFile._read(self, size, read_method)
  2280.  
  2281.  
  2282.  
  2283. def _lock_file(f, dotlock = True):
  2284.     '''Lock file f using lockf and dot locking.'''
  2285.     dotlock_done = False
  2286.     
  2287.     try:
  2288.         if fcntl:
  2289.             
  2290.             try:
  2291.                 fcntl.lockf(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
  2292.             except IOError:
  2293.                 e = None
  2294.                 if e.errno in (errno.EAGAIN, errno.EACCES):
  2295.                     raise ExternalClashError('lockf: lock unavailable: %s' % f.name)
  2296.                 e.errno in (errno.EAGAIN, errno.EACCES)
  2297.                 raise 
  2298.             except:
  2299.                 None<EXCEPTION MATCH>IOError
  2300.             
  2301.  
  2302.         None<EXCEPTION MATCH>IOError
  2303.         if dotlock:
  2304.             
  2305.             try:
  2306.                 pre_lock = _create_temporary(f.name + '.lock')
  2307.                 pre_lock.close()
  2308.             except IOError:
  2309.                 e = None
  2310.                 if e.errno == errno.EACCES:
  2311.                     return None
  2312.                 raise 
  2313.             except:
  2314.                 e.errno == errno.EACCES
  2315.  
  2316.             
  2317.             try:
  2318.                 if hasattr(os, 'link'):
  2319.                     os.link(pre_lock.name, f.name + '.lock')
  2320.                     dotlock_done = True
  2321.                     os.unlink(pre_lock.name)
  2322.                 else:
  2323.                     os.rename(pre_lock.name, f.name + '.lock')
  2324.                     dotlock_done = True
  2325.             except OSError:
  2326.                 e.errno == errno.EACCES
  2327.                 e = e.errno == errno.EACCES
  2328.                 if (e.errno == errno.EEXIST or os.name == 'os2') and e.errno == errno.EACCES:
  2329.                     os.remove(pre_lock.name)
  2330.                     raise ExternalClashError('dot lock unavailable: %s' % f.name)
  2331.                 e.errno == errno.EACCES
  2332.                 raise 
  2333.             except:
  2334.                 e.errno == errno.EACCES<EXCEPTION MATCH>OSError
  2335.             
  2336.  
  2337.         e.errno == errno.EACCES
  2338.     except:
  2339.         if fcntl:
  2340.             fcntl.lockf(f, fcntl.LOCK_UN)
  2341.         
  2342.         if dotlock_done:
  2343.             os.remove(f.name + '.lock')
  2344.         
  2345.         raise 
  2346.  
  2347.  
  2348.  
  2349. def _unlock_file(f):
  2350.     '''Unlock file f using lockf and dot locking.'''
  2351.     if fcntl:
  2352.         fcntl.lockf(f, fcntl.LOCK_UN)
  2353.     
  2354.     if os.path.exists(f.name + '.lock'):
  2355.         os.remove(f.name + '.lock')
  2356.     
  2357.  
  2358.  
  2359. def _create_carefully(path):
  2360.     """Create a file if it doesn't exist and open for reading and writing."""
  2361.     fd = os.open(path, os.O_CREAT | os.O_EXCL | os.O_RDWR, 438)
  2362.     
  2363.     try:
  2364.         return open(path, 'rb+')
  2365.     finally:
  2366.         os.close(fd)
  2367.  
  2368.  
  2369.  
  2370. def _create_temporary(path):
  2371.     '''Create a temp file based on path and open for reading and writing.'''
  2372.     return _create_carefully('%s.%s.%s.%s' % (path, int(time.time()), socket.gethostname(), os.getpid()))
  2373.  
  2374.  
  2375. def _sync_flush(f):
  2376.     '''Ensure changes to file f are physically on disk.'''
  2377.     f.flush()
  2378.     if hasattr(os, 'fsync'):
  2379.         os.fsync(f.fileno())
  2380.     
  2381.  
  2382.  
  2383. def _sync_close(f):
  2384.     '''Close file f, ensuring all changes are physically on disk.'''
  2385.     _sync_flush(f)
  2386.     f.close()
  2387.  
  2388.  
  2389. class _Mailbox:
  2390.     
  2391.     def __init__(self, fp, factory = rfc822.Message):
  2392.         self.fp = fp
  2393.         self.seekp = 0
  2394.         self.factory = factory
  2395.  
  2396.     
  2397.     def __iter__(self):
  2398.         return iter(self.next, None)
  2399.  
  2400.     
  2401.     def next(self):
  2402.         while None:
  2403.             
  2404.             try:
  2405.                 self._search_start()
  2406.             except EOFError:
  2407.                 self.seekp = self.fp.tell()
  2408.                 return None
  2409.  
  2410.             start = self.fp.tell()
  2411.             self._search_end()
  2412.             self.seekp = stop = self.fp.tell()
  2413.             if start != stop:
  2414.                 break
  2415.                 continue
  2416.             continue
  2417.             return self.factory(_PartialFile(self.fp, start, stop))
  2418.  
  2419.  
  2420.  
  2421. class UnixMailbox(_Mailbox):
  2422.     
  2423.     def _search_start(self):
  2424.         while None:
  2425.             pos = self.fp.tell()
  2426.             line = self.fp.readline()
  2427.             if not line:
  2428.                 raise EOFError
  2429.             if line[:5] == 'From ' and self._isrealfromline(line):
  2430.                 self.fp.seek(pos)
  2431.                 return None
  2432.             continue
  2433.             return None
  2434.  
  2435.     
  2436.     def _search_end(self):
  2437.         self.fp.readline()
  2438.         while None:
  2439.             pos = self.fp.tell()
  2440.             line = self.fp.readline()
  2441.             if not line:
  2442.                 return None
  2443.             if line[:5] == 'From ' and self._isrealfromline(line):
  2444.                 self.fp.seek(pos)
  2445.                 return None
  2446.             continue
  2447.             return None
  2448.  
  2449.     _fromlinepattern = 'From \\s*[^\\s]+\\s+\\w\\w\\w\\s+\\w\\w\\w\\s+\\d?\\d\\s+\\d?\\d:\\d\\d(:\\d\\d)?(\\s+[^\\s]+)?\\s+\\d\\d\\d\\d\\s*[^\\s]*\\s*$'
  2450.     _regexp = None
  2451.     
  2452.     def _strict_isrealfromline(self, line):
  2453.         if not self._regexp:
  2454.             import re as re
  2455.             self._regexp = re.compile(self._fromlinepattern)
  2456.         
  2457.         return self._regexp.match(line)
  2458.  
  2459.     
  2460.     def _portable_isrealfromline(self, line):
  2461.         return True
  2462.  
  2463.     _isrealfromline = _strict_isrealfromline
  2464.  
  2465.  
  2466. class PortableUnixMailbox(UnixMailbox):
  2467.     _isrealfromline = UnixMailbox._portable_isrealfromline
  2468.  
  2469.  
  2470. class MmdfMailbox(_Mailbox):
  2471.     
  2472.     def _search_start(self):
  2473.         while None:
  2474.             line = self.fp.readline()
  2475.             if not line:
  2476.                 raise EOFError
  2477.             if line[:5] == '\x01\x01\x01\x01\n':
  2478.                 return None
  2479.             continue
  2480.             return None
  2481.  
  2482.     
  2483.     def _search_end(self):
  2484.         while None:
  2485.             pos = self.fp.tell()
  2486.             line = self.fp.readline()
  2487.             if not line:
  2488.                 return None
  2489.             if line == '\x01\x01\x01\x01\n':
  2490.                 self.fp.seek(pos)
  2491.                 return None
  2492.             continue
  2493.             return None
  2494.  
  2495.  
  2496.  
  2497. class MHMailbox:
  2498.     
  2499.     def __init__(self, dirname, factory = rfc822.Message):
  2500.         import re
  2501.         pat = re.compile('^[1-9][0-9]*$')
  2502.         self.dirname = dirname
  2503.         list = os.listdir(self.dirname)
  2504.         list = filter(pat.match, list)
  2505.         list = map(long, list)
  2506.         list.sort()
  2507.         self.boxes = map(str, list)
  2508.         self.boxes.reverse()
  2509.         self.factory = factory
  2510.  
  2511.     
  2512.     def __iter__(self):
  2513.         return iter(self.next, None)
  2514.  
  2515.     
  2516.     def next(self):
  2517.         if not self.boxes:
  2518.             return None
  2519.         fn = self.boxes.pop()
  2520.         fp = open(os.path.join(self.dirname, fn))
  2521.         msg = self.factory(fp)
  2522.         
  2523.         try:
  2524.             msg._mh_msgno = fn
  2525.         except (AttributeError, TypeError):
  2526.             self.boxes
  2527.             self.boxes
  2528.         except:
  2529.             self.boxes
  2530.  
  2531.         return msg
  2532.  
  2533.  
  2534.  
  2535. class BabylMailbox(_Mailbox):
  2536.     
  2537.     def _search_start(self):
  2538.         while None:
  2539.             line = self.fp.readline()
  2540.             if not line:
  2541.                 raise EOFError
  2542.             if line == '*** EOOH ***\n':
  2543.                 return None
  2544.             continue
  2545.             return None
  2546.  
  2547.     
  2548.     def _search_end(self):
  2549.         while None:
  2550.             pos = self.fp.tell()
  2551.             line = self.fp.readline()
  2552.             if not line:
  2553.                 return None
  2554.             if line == '\x1f\x0c\n' or line == '\x1f':
  2555.                 self.fp.seek(pos)
  2556.                 return None
  2557.             continue
  2558.             return None
  2559.  
  2560.  
  2561.  
  2562. class Error(Exception):
  2563.     '''Raised for module-specific errors.'''
  2564.     pass
  2565.  
  2566.  
  2567. class NoSuchMailboxError(Error):
  2568.     """The specified mailbox does not exist and won't be created."""
  2569.     pass
  2570.  
  2571.  
  2572. class NotEmptyError(Error):
  2573.     '''The specified mailbox is not empty and deletion was requested.'''
  2574.     pass
  2575.  
  2576.  
  2577. class ExternalClashError(Error):
  2578.     '''Another process caused an action to fail.'''
  2579.     pass
  2580.  
  2581.  
  2582. class FormatError(Error):
  2583.     '''A file appears to have an invalid format.'''
  2584.     pass
  2585.  
  2586.